package com.example.recommendationservice.service.impl;

import com.alibaba.dashscope.embeddings.*;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.example.recommendationservice.entity.ProductsTags;
import com.example.recommendationservice.entity.UserBrowseRecord;
import com.example.recommendationservice.entity.UserPurchaseRecord;
import com.example.recommendationservice.entity.UsersTags;
import com.example.recommendationservice.repository.ProductsTagsRepository;
import com.example.recommendationservice.service.LLMService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Service
public class LLMServiceImpl implements LLMService {

    @Autowired
    private ProductsTagsRepository productsTagsRepository;

    private static final int DASHSCOPE_MAX_BATCH_SIZE = 10;

    private static final String API_KEY = "sk-292540dbd23340ea929af7c8fe181b62";

    // 基于内容推荐阈值
    private static final double A = 0.6;

    // 混合推荐基于内容推荐权重
    private static final double B = 0.2;

    // 混合推荐基于协同过滤推荐权重
    private static final double C = 0.8;

    @Override
    public List<String> getRecommendations(UsersTags usersTags,List<UsersTags> allUsersTags, List<ProductsTags> allProductsTags, List<UserBrowseRecord> userBrowseRecords, List<UserPurchaseRecord> userPurchaseRecords, List<UserBrowseRecord> allUserBrowseRecords , List<UserPurchaseRecord> allUserPurchaseRecords) {

        //获取基于内容推荐的商品列表
        List<String> contentBasedRecommendations = contentBasedRecommendation(usersTags, allProductsTags);

        // 获取基于协同过滤推荐的商品列表
        List<String> collaborativeFilteringRecommendations = collaborativeFilteringRecommendation(usersTags, allUsersTags, allProductsTags, userBrowseRecords, userPurchaseRecords, allUserBrowseRecords, allUserPurchaseRecords);

        // 混合推荐
        return hybridRecommendation(contentBasedRecommendations, collaborativeFilteringRecommendations);
    }

    //获取特征向量
    private List<List<Double>> gteLLM(List<String> inputs) {

        TextEmbeddingResult result = null;
        int batchCounter = 0;

        for (int i = 0; i < inputs.size(); i += DASHSCOPE_MAX_BATCH_SIZE) {
            List<String> batch = inputs.subList(i, Math.min(i + DASHSCOPE_MAX_BATCH_SIZE, inputs.size()));
            TextEmbeddingParam param = TextEmbeddingParam.builder()
                    .apiKey(API_KEY)
                    .model(TextEmbedding.Models.TEXT_EMBEDDING_V3)
                    .texts(batch)
                    .build();

            TextEmbedding textEmbedding = new TextEmbedding();
            try {
                TextEmbeddingResult resp = textEmbedding.call(param);
                if (resp != null) {
                    if (result == null) {
                        result = resp;
                    } else {
                        for (var emb : resp.getOutput().getEmbeddings()) {
                            emb.setTextIndex(emb.getTextIndex() + batchCounter);
                            result.getOutput().getEmbeddings().add(emb);
                        }
                        result.getUsage().setTotalTokens(result.getUsage().getTotalTokens() + resp.getUsage().getTotalTokens());
                    }
                } else {
                    System.out.println(resp);
                }
            } catch (ApiException | NoApiKeyException e) {
                e.printStackTrace();
            }
            batchCounter += batch.size();
        }

        TextEmbeddingOutput output = result.getOutput();
        List<TextEmbeddingResultItem> resultItems = output.getEmbeddings();
        List<List<Double>> batchEmbeddings = resultItems.stream()
                .map(TextEmbeddingResultItem::getEmbedding)
                .collect(Collectors.toList());

        return batchEmbeddings;
    }

    //基于内容推荐数据处理
    private List<String> contentBasedRecommendation(UsersTags usersTags, List<ProductsTags> allProductsTags) {

        //目标用户分类
        List<String> usersFavoriteCategoriesList = Collections.singletonList(usersTags.getFavoriteCategories());

        //获取用户特征向量
        List<List<Double>> usersFavoriteCategoriesEmbeddings = gteLLM(usersFavoriteCategoriesList);

        //所有产品分类
        List<String> allProductsCategoriesList = new ArrayList<>();
        for (ProductsTags product : allProductsTags) {
            allProductsCategoriesList.add(product.getCategory());
        }

        //所有产品特征向量
        List<List<Double>> allProductsCategoriesEmbeddings = gteLLM(allProductsCategoriesList);

        //获取基于内容推荐的商品ID列表
        return getContentBasedRecommendationProductId(usersFavoriteCategoriesEmbeddings, allProductsCategoriesEmbeddings, allProductsTags);
    }

    // 获取基于内容推荐的商品ID列表
    private List<String> getContentBasedRecommendationProductId(List<List<Double>> usersFavoriteCategoriesEmbeddings, List<List<Double>>  allProductsCategoriesEmbeddings, List<ProductsTags> allProductsTags) {

        List<Double> promptEmbedding = usersFavoriteCategoriesEmbeddings.get(0);

        //计算相似度
        Map<Integer, Double> similarities = calculateSimilarities(promptEmbedding, allProductsCategoriesEmbeddings);
        //过滤
        List<Map.Entry<Integer, Double>> filteredSimilarities = filterSimilarities(similarities);
        //降序排列
        filteredSimilarities.sort((entry1, entry2) -> Double.compare(entry2.getValue(), entry1.getValue()));

        List<String> productIds = new ArrayList<>();
        for (Map.Entry<Integer, Double> entry : filteredSimilarities) {
            int index = entry.getKey();
            ProductsTags product = allProductsTags.get(index);
            productIds.add(product.getId());
        }

        return productIds;
    }

    // 优化相似度计算
    private Map<Integer, Double> calculateSimilarities(List<Double> promptEmbedding, List<List<Double>> allEmbeddings) {
        return IntStream.range(1, allEmbeddings.size())
                .parallel()
                .boxed()
                .collect(Collectors.toMap(
                        i -> i - 1,
                        i -> {
                            List<Double> productEmbedding = allEmbeddings.get(i);

                            return cosineSimilarity(promptEmbedding, productEmbedding);
                        }
                ));
    }

    // 计算余弦相似度
    private double cosineSimilarity(List<Double> vectorA, List<Double> vectorB) {
        double dotProduct = 0.0;
        double normA = 0.0;
        double normB = 0.0;
        for (int i = 0; i < vectorA.size(); i++) {
            dotProduct += vectorA.get(i) * vectorB.get(i);
            normA += Math.pow(vectorA.get(i), 2);
            normB += Math.pow(vectorB.get(i), 2);
        }
        normA = Math.sqrt(normA);
        normB = Math.sqrt(normB);

        return dotProduct / (normA * normB);
    }

    //过滤掉相似度低于阈值 V 的条目
    private List<Map.Entry<Integer, Double>> filterSimilarities(Map<Integer, Double> similarities) {
        List<Map.Entry<Integer, Double>> filteredSimilarities = new ArrayList<>();
        for (Map.Entry<Integer, Double> entry : similarities.entrySet()) {
            if (entry.getValue() >= A) {
                filteredSimilarities.add(entry);
            }
        }
        return filteredSimilarities;
    }

    // 基于协同过滤的推荐
    private List<String> collaborativeFilteringRecommendation(UsersTags usersTags,List<UsersTags> allUsersTags, List<ProductsTags> allProductsTags,
                                                              List<UserBrowseRecord> userBrowseRecords, List<UserPurchaseRecord> userPurchaseRecords,
                                                              List<UserBrowseRecord> allUserBrowseRecords , List<UserPurchaseRecord> allUserPurchaseRecords) {
        // 构建所有用户和产品矩阵
        Map<String, Map<String, Double>> userProductMatrix = buildUserProductMatrix(allUserBrowseRecords, allUserPurchaseRecords);

        // 计算用户之间浏览购买的相似度
        Map<String, Double> userSimilarities = calculateUserSimilarities(usersTags.getId(), userProductMatrix);

        // 根据相似用户的浏览记录和购买记录推荐的商品ID列表
        return recommendProductsBasedOnSimilarUsers(usersTags.getId(), userSimilarities, userProductMatrix, allProductsTags);
    }
    // 构建用户和产品矩阵
    private Map<String, Map<String, Double>> buildUserProductMatrix(List<UserBrowseRecord> allBrowseRecords, List<UserPurchaseRecord> allPurchaseRecords) {
        Map<String, Map<String, Double>> matrix = new HashMap<>();

        // 遍历浏览记录和购买记录,构建用户产品矩阵,多次浏览或购买相同产品时简单相加,浏览1,购买3

        for (UserBrowseRecord record : allBrowseRecords) {
            String userId = record.getUserId();
            String productId = record.getProductId();
            matrix.computeIfAbsent(userId, k -> new HashMap<>())
                    .merge(productId, 1.0, Double::sum);
        }
        for (UserPurchaseRecord record : allPurchaseRecords) {
            String userId = record.getUserId();
            String productId = record.getProductId();
            matrix.computeIfAbsent(userId, k -> new HashMap<>())
                    .merge(productId, 3.0, Double::sum);
        }
        return matrix;
    }

    // 计算用户之间的相似度
    private Map<String, Double> calculateUserSimilarities(String userId, Map<String, Map<String, Double>> userProductMatrix) {
        Map<String, Double> similarities = new HashMap<>();

        //目标用户浏览购买的产品向量
        Map<String, Double> targetUserVector = userProductMatrix.getOrDefault(userId, new HashMap<>());
        for (Map.Entry<String, Map<String, Double>> entry : userProductMatrix.entrySet()) {
            String otherUserId = entry.getKey();
            if (!otherUserId.equals(userId)) {
                //其他用户浏览购买的产品向量
                Map<String, Double> otherUserVector = entry.getValue();

                List<Double> targetVector = buildVector(targetUserVector);
                List<Double> otherVector = buildVector(otherUserVector);
                double similarity = cosineSimilarity(targetVector, otherVector);

                similarities.put(otherUserId, similarity);
            }
        }
        return similarities;
    }

    // 构建向量
    private List<Double> buildVector(Map<String, Double> userProductMap) {
        // 获取所有的产品 ID 列表
        List<String> allProductIds = getAllProductIds();

        //从 userProductMap 中获取用户id对该产品的交互得分，没有对应id，则添加id和 0
        List<Double> vector = new ArrayList<>();
        for (String productId : allProductIds) {
            vector.add(userProductMap.getOrDefault(productId, 0.0));
        }
        return vector;
    }

    // 获取所有的产品 ID 列表
    private List<String> getAllProductIds() {

        List<ProductsTags> allProductsTags = productsTagsRepository.findAll();

        List<String> allProductsId = new ArrayList<>();
        for (ProductsTags product : allProductsTags) {
            allProductsId.add(product.getId());
        }

        return allProductsId;
    }

    // 根据相似用户的浏览记录和购买记录推荐产品
    private List<String> recommendProductsBasedOnSimilarUsers(String userId, Map<String, Double> userSimilarities,
                                                              Map<String, Map<String, Double>> userProductMatrix, List<ProductsTags> allProductsTags) {
        Map<String, Double> productScores = new HashMap<>();
        for (Map.Entry<String, Double> entry : userSimilarities.entrySet()) {
            //相似用户的id和向量
            String otherUserId = entry.getKey();
            double similarity = entry.getValue();

            //计算不同用户得分
            Map<String, Double> otherUserProducts = userProductMatrix.get(otherUserId);
            for (Map.Entry<String, Double> productEntry : otherUserProducts.entrySet()) {
                String productId = productEntry.getKey();
                //计算得分, 相似度乘以该用户的交互得分
                double score = productEntry.getValue() * similarity;
                productScores.put(productId, productScores.getOrDefault(productId, 0.0) + score);
            }
        }

        return productScores.entrySet().stream()
                // 按得分降序排列
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
    }

    // 混合推荐
    private List<String> hybridRecommendation(List<String> contentBasedRecommendations, List<String> collaborativeFilteringRecommendations) {
        Map<String, Double> productRanks = new HashMap<>();

        // 处理基于内容的推荐结果
        for (int i = 0; i < contentBasedRecommendations.size(); i++) {
            String productId = contentBasedRecommendations.get(i);
            // 用列表长度减去索引，让相似度高的商品得分高
            double contentRank = (contentBasedRecommendations.size() - i) * B;
            productRanks.put(productId, contentRank);
        }

        // 处理基于协同过滤的推荐结果
        for (int i = 0; i < collaborativeFilteringRecommendations.size(); i++) {
            String productId = collaborativeFilteringRecommendations.get(i);
            // 用列表长度减去索引，让得分高的商品得分高
            double collaborativeRank = (collaborativeFilteringRecommendations.size() - i) * C;
            productRanks.put(productId, productRanks.getOrDefault(productId, 0.0) + collaborativeRank);
        }

        return productRanks.entrySet().stream()
                .sorted(Map.Entry.<String, Double>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .limit(4)
                .collect(Collectors.toList());
    }

}
